#include <stdlib.h>
#include <pthread.h>
#include "dig_mem_pool.h"
#include "dig_ring.h"
#include "zip_utils.h"



/*
 * mem pool manage several raw memory block, each block will contain fix size memory element which
 * will be allocate for end user.
*/



#define MAX_RAW_BLOCK_COUNT 10
#define ALIGN_PAD(size) \
    (((size) % sizeof(void *) == 0) ? 0 : (((size) | ~-(int)sizeof(void *)) + 1 - (size)))

typedef struct mem_elem
{
	RING_ENTRY(mem_elem)	link;

}mem_elem_t;

RING_HEAD(mem_elem_list,mem_elem);
typedef struct mem_elem_list mem_elem_list_t;
struct mem_pool
{
	uint32_t		    elem_size;
	uint32_t		    elem_count;

	mem_elem_list_t 	free_list;
	pthread_mutex_t 	list_lock;

    void                *raw_mem;
};


static void fill_free_list(mem_pool_t *mp, uint32_t elem_count);

mem_pool_t * 
mem_pool_create(uint32_t elem_size, unsigned int elem_count)
{
	mem_pool_t *mp;
    DIG_CALLOC(mp, 1, sizeof(mem_pool_t));

	int size = elem_size > sizeof(mem_elem_t) ? elem_size : sizeof(mem_elem_t);
    mp->elem_size = size + ALIGN_PAD(size);
	mp->elem_count = 0;

	RING_INIT(&mp->free_list,mem_elem,link);
	fill_free_list(mp,elem_count);

	pthread_mutex_init(&mp->list_lock,NULL);
	return mp;
}


static void 
fill_free_list(mem_pool_t *mp,  uint32_t elem_count)
{
	void *new_block = calloc(elem_count, mp->elem_size);
    ASSERT(new_block, "alloc memory for memory pool failed");
    int i;
    for (i = 0;i < elem_count; ++i)
    {
        mem_elem_t *e = (mem_elem_t *)((char *)new_block + i * mp->elem_size);
        RING_ELEM_INIT(e,link);
        RING_INSERT_TAIL(&mp->free_list,e,mem_elem,link);
    }
    mp->raw_mem = new_block;
    mp->elem_count += elem_count;
}

void 
mem_pool_delete(mem_pool_t *mp)
{
    ASSERT(mp,"delete empty mem pool");
	pthread_mutex_destroy(&mp->list_lock);
    free(mp->raw_mem);
	free(mp);
}

void *
mem_pool_alloc(mem_pool_t *mp)
{
    ASSERT(mp,"alloc memory from empy mem pool");

	pthread_mutex_lock(&mp->list_lock);
    mem_elem_t *e = NULL;
	if (!RING_EMPTY(&mp->free_list,mem_elem,link))
	{
	    e = RING_FIRST(&mp->free_list);
	    RING_REMOVE(e,link);
	}
	pthread_mutex_unlock(&mp->list_lock);

	return (void *)e;
}


void 
mem_pool_free(mem_pool_t *mp, void *mem)
{
    ASSERT(mp,"free memory to empty mem pool");
    ASSERT(mem, "free empty memory to mem pool");

	pthread_mutex_lock(&mp->list_lock);
	RING_INSERT_TAIL(&mp->free_list,(mem_elem_t *)mem, mem_elem, link);
	pthread_mutex_unlock(&mp->list_lock);
}
